import java.util.Set;
import java.util.Iterator;
import java.net.InetSocketAddress;
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.util.Arrays;

// TODO
/*
    1. Socekt 对应的应该是 tcp 或 udp 报文
    2. 在 http 协议中，可能一次传输的内容分散到两个 tcp 报文中，这种情况下 下面的写法是无法正常解析相应的内容的。
*/
public class ServerCore {
    private CommonUtil colorPrintln = new CommonUtil();
	public static void main(String[] args) {
        try {
            new ServerCore().start();
        } catch (Exception e) {
            System.err.println("[Simpe Server Exception]");
            e.printStackTrace();
        }
	}
	
	public void start() throws Exception {
		ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
		serverSocketChannel.socket().bind(new InetSocketAddress(8088));	
        System.out.println(this.colorPrintln.render("[Simple Java Server]will listen 8088", PrintColor.GREEN));
		serverSocketChannel.configureBlocking(false);
		
		Selector selector = Selector.open();
		SelectionKey mainKey = serverSocketChannel.register(selector, serverSocketChannel.validOps());

		while (true) {
			int readyChannels = selector.select();
			if (readyChannels == 0) continue;
			Set<SelectionKey> selectedKeys = selector.selectedKeys();
			Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

			while (keyIterator.hasNext()) {
				SelectionKey key = keyIterator.next();
				if (key.isAcceptable()) {
                    System.out.println("\n\nNew Connect Acceptable..");
                    SocketChannel newConnect = serverSocketChannel.accept();
                    newConnect.configureBlocking(false);
                    //newConnect.register(selector, newConnect.validOps());
                    newConnect.register(selector, SelectionKey.OP_READ);
				} else if (key.isConnectable()) {
                    System.out.println("New Connect established..");
				} else if (key.isReadable()) {
                    SocketChannel readableChannel = (SocketChannel)key.channel();
                    System.out.println("Begin read from connect..." + readableChannel);
                    // TODO 此处需要考虑合适的 Buffer 大小。
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    
                    StringBuilder builder = new StringBuilder();
                    System.out.println("\n\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>READ QAQ ONE<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<");
                    int totalSize = 0;
                    while(true) {
                        int readCount = readableChannel.read(buffer);
                        totalSize = totalSize + readCount;
                        buffer.flip();

                        // 此处应该考虑合适的取出 buffer 内容的方法， 目前这样做是因为 clear 等操作并没有实际清空 buffer 中的所有内容
                        String readData = null;
                        if (readCount > 0) {
                            if (readCount == 256) {
                                readData = new String(buffer.array());
                            } else {
                                readData = new String(Arrays.copyOf(buffer.array(), readCount));
                            }
                            builder.append(readData);

                            //System.out.println(">>> This time read count: " + readCount + " content is:");
                            //System.out.println(readData);
                        }

                        buffer.clear();
                        if (readCount == -1 || readCount < 256) {
                            break;
                        }
                    }
                    colorPrintln.info("Total Size:" + totalSize);
                    //System.out.println("---------------------RECEIVEDATA BEGIN-------------------");
                    //System.out.println(builder);
                    //System.out.println("---------------------RECEIVEDATA END-------------------");
                    ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
                    writeBuffer.put(this.generateResponse("Success!^_^").getBytes());
                    writeBuffer.flip();
                    readableChannel.write(writeBuffer);
                    
                    readableChannel.close();
                    System.out.println("Close the connect:" + readableChannel);

                    //System.out.println(">>response:" + new String(writeBuffer.array()));
                } else if (key.isWritable()) {
                    SocketChannel writableChannel = (SocketChannel)key.channel();
                    System.out.println("Begin wirte to channel..." + writableChannel);
                    ByteBuffer buffer = ByteBuffer.allocate(256);
                    String content = this.generateResponse("Success^_^");
                    buffer.put(content.getBytes());
                    buffer.flip();
                    writableChannel.write(buffer);
                    writableChannel.close();
                    System.out.println("Close the connect:" + writableChannel);
				}
				keyIterator.remove();
			}
		}
	}

    private String generateResponse(String data) {
        String defaultResponse = "HTTP/1.1 200 OK\r\n";
        String serverMessage = "Server: HHG-SERVER\r\n";
        String contentType = "Content-Type: text/html\r\n\r\n";

        StringBuilder responseBuilder = new StringBuilder();
        responseBuilder.append(defaultResponse);
        responseBuilder.append(serverMessage);
        responseBuilder.append(contentType);
        responseBuilder.append(data);
        return responseBuilder.toString();
    }
}
